package vcsn;

import java.util.ArrayList;
import java.util.List;

import vcsn.entities.Class;
import vcsn.entities.Method;
import vcsn.entities.Relation;
import vcsn.entities.RelationMapDiff;
import vcsn.entities.RelationMapDiff.EntityType;
import vcsn.entities.Variable;

public class RelationMapComparer {
	public RelationMapComparer(){
		
	}
	
	public List<String> getDiffPatchInflectedFileList(List<RelationMapDiff> relationMapDiffList, List<Class> referenceRelationMap){
		List<String> inflectedFileList = new ArrayList<String>();
		for(RelationMapDiff diffEntry : relationMapDiffList){
			if(diffEntry.entityType.equals(EntityType.CLASS)){
				for(Class classEntity : referenceRelationMap){
					if(diffEntry.entityIdentity.equals(classEntity.id)){
						if(!inflectedFileList.contains(classEntity.filename)){
							inflectedFileList.add(classEntity.filename);
						}
						break;
					}
				}
			}else if(diffEntry.entityType.equals(EntityType.METHOD)){
				boolean done = false;
				for(Class classEntity : referenceRelationMap){
					if(done){
						break;
					}
					for(Method declaredMethod : classEntity.methods){
						String qualifiedMethodSignature = declaredMethod.id + Config.separator;
						for(Relation parameter : declaredMethod.parameterDependency){
							qualifiedMethodSignature += parameter.targetId + Config.separator;
						}
						if(diffEntry.entityIdentity.equals(qualifiedMethodSignature)){
							if(!inflectedFileList.contains(classEntity.filename)){
								inflectedFileList.add(classEntity.filename);
								done = true;
							}
							break;
						}
					}
				}
			}else if(diffEntry.entityType.equals(EntityType.VARIABLE)){
				boolean done = false;
				for(Class classEntity : referenceRelationMap){
					if(done){
						break;
					}
					for(Variable declaredVariable : classEntity.variables){
						if(diffEntry.entityIdentity.equals(declaredVariable.id)){
							if(!inflectedFileList.contains(classEntity.filename)){
								 done = true;
							}
							break;
						}
					}
				}
			}
		}
		
		return inflectedFileList;
		
	}
	
	public List<RelationMapDiff> compare(List<Class> relationMapOld, List<Class> relationMapNew){
		List<RelationMapDiff> diffList = new ArrayList<RelationMapDiff>();
		compareDiff(diffList, relationMapOld, relationMapNew, RelationMapDiff.Status.REMOVE);
		compareDiff(diffList, relationMapNew, relationMapOld, RelationMapDiff.Status.ADD);
		return diffList;
	}
	
	private void compareDiff(List<RelationMapDiff> diffList, List<Class> relationMapOne, List<Class> relationMapTwo, RelationMapDiff.Status status){
		for(Class c : relationMapOne){
			boolean isClassExist = false;
			Class refClass = null;
			for(Class ref : relationMapTwo){
				if(c.id.equals(ref.id)){
					isClassExist = true;
					refClass = ref;
					break;
				}
			}
			if(!isClassExist){
				for(int i = 0; i < c.variables.length;i++){
					diffList = addDiffEntry(diffList, RelationMapDiff.EntityType.VARIABLE, c.variables[i].id, c.variables[i].association.targetId, c.variables[i].modifier, c.variables[i].declarationLineNumber, status);
				}
				for(int i = 0; i < c.methods.length;i++){
					diffList = addDiffEntry(diffList, RelationMapDiff.EntityType.METHOD, c.methods[i].id, c.methods[i].returnValueDependency.targetId, c.methods[i].modifier, c.methods[i].declarationLineNumber, status);
				}
				diffList = addDiffEntry(diffList, RelationMapDiff.EntityType.CLASS, c.id, c.id, c.modifier, c.declarationLineNumber, status);
			}else{
				for(Variable var : c.variables){
					boolean isVaribleExist = false;
					for(Variable refVar : refClass.variables){
						if(refVar.id.equals(var.id)){
							isVaribleExist = true;
							if(var.modifier != refVar.modifier){
								isVaribleExist = false;
							}
							if(isVaribleExist){
								break;
							}
						}
					}
					if(!isVaribleExist){
						diffList = addDiffEntry(diffList, RelationMapDiff.EntityType.VARIABLE, var.id, var.association.targetId, var.modifier, var.declarationLineNumber, status);
					}
				}
				
				for(Method method : c.methods){
					boolean isMethodExist = false;
					for(Method refMethod : refClass.methods){
						if(refMethod.id.equals(method.id)){
							isMethodExist = true;
							if(method.modifier != refMethod.modifier){
								isMethodExist = false;
							}
							if(method.returnValueDependency != null && !method.returnValueDependency.targetId.equals(refMethod.returnValueDependency.targetId)){
								isMethodExist = false;
							}
							if(method.parameterDependency.length != refMethod.parameterDependency.length){
								isMethodExist = false;
							}
							if(isMethodExist){
								for(int i = 0; i < method.parameterDependency.length;i++){
									if(method.parameterDependency[i].targetId.equals(refMethod.parameterDependency[i].targetId)){
										isMethodExist = true;
									}else{
										isMethodExist = false;
										break;
									}
								}
							}
							if(isMethodExist){
								break;
							}
						}
					}
					if(!isMethodExist){
						String qualifiedMethodSignature = method.id + Config.separator;
						for(Relation parameter : method.parameterDependency){
							qualifiedMethodSignature += parameter.targetId + Config.separator;
						}
//						if(method.returnValueDependency != null){
//							qualifiedMethodSignature += method.returnValueDependency.targetId + Config.separator;
//						}
						diffList = addDiffEntry(diffList, RelationMapDiff.EntityType.METHOD, qualifiedMethodSignature, method.returnValueDependency.targetId, method.modifier, method.declarationLineNumber, status);
					}
				}
			}
		}
	}
	
	private List<RelationMapDiff> addDiffEntry(List<RelationMapDiff> diffList, RelationMapDiff.EntityType type, String id, String typeDefined, int modifier, int declarationLineNumber, RelationMapDiff.Status status){
		RelationMapDiff diffEntry = new RelationMapDiff();
		diffEntry.entityType = type;
		diffEntry.entityIdentity = id;
		diffEntry.modifier = modifier;
		diffEntry.declarationLineNumber = declarationLineNumber;
		diffEntry.entityStatus = status;
		diffEntry.type = typeDefined;
		diffList.add(diffEntry);
		return diffList;
	}
}
